package redis

import (
	"context"
	"fmt"
	"github.com/redis/go-redis/v9"
	"time"
)

// 声明一个全局的 rdb 变量
var rdb *redis.Client

// 初始化连接 rdb1 *redis.Client,
func InitRedisClient() (err error) {
	// NewClient将客户端返回给Options指定的Redis Server。
	// Options保留设置以建立redis连接。
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "sky541514", // 没有密码，默认值
		DB:       1,           // 默认DB 0 连接到服务器后要选择的数据库。
		PoolSize: 20,          // 最大套接字连接数。 默认情况下，每个可用CPU有10个连接，由runtime.GOMAXPROCS报告。
	})

	// Background返回一个非空的Context。它永远不会被取消，没有值，也没有截止日期。
	// 它通常由main函数、初始化和测试使用，并作为传入请求的顶级上下文
	ctx := context.Background()

	_, err = rdb.Ping(ctx).Result()
	if err != nil {
		return err
	}
	return nil
}

/**string set*/
func RedisSetKey(key string, val string) error {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()
	// 设置Redis ' Set key value [expiration] '命令。
	err := rdb.Set(ctx, key, val, time.Hour).Err()
	if err != nil {
		fmt.Printf("redis set failed, err: %v\n", err)
		return err
	}
	return nil
}

/**string get*/
func RedisGetKey(key string) (string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	val, err := rdb.Get(ctx, key).Result()
	if err != nil {
		if err == redis.Nil {
			return "", nil
			// DeadlineExceeded是Context返回的错误。当上下文的截止日期过去时发生错误。
		} else if err == context.DeadlineExceeded {
			return "", fmt.Errorf("获取值超时")
		} else {
			return "", fmt.Errorf("获取值失败: %v", err)
		}
	}

	if val == "" {
		return "", nil
	}

	return val, nil
}

/**hash get*/
func HGetDemo(key string) (map[string]string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	val, err := rdb.HGetAll(ctx, key).Result()
	if err != nil {
		// redis.Nil
		// 其它错误
		fmt.Printf("hgetall failed, err: %v\n", err)
		return nil, err
	}
	return val, nil
}

/**hashmap set*/
func HSetDemo(key string, value string) error {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	err := rdb.HSet(ctx, key, value).Err()
	if err != nil {
		// redis.Nil
		// 其它错误
		fmt.Printf("hgetall failed, err: %v\n", err)
		return err
	}
	return nil
}

/**hash map get*/
func HMGetDemo() {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	val := rdb.HMGet(ctx, "user", "name", "age").Val()
	fmt.Printf("redis HMGet %v\n", val)

	value := rdb.HGet(ctx, "user", "age").Val()
	fmt.Printf("redis HGet value: %v\n", value)
}

/**hsah map set*/
func HMSetDemo(key string, value string) error {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	err := rdb.HMSet(ctx, key, value).Err()
	fmt.Printf("redis HMSet %v\n", err)

	return err
}

/* zset Demo 操作 zset 示例*/
func ZSetDemo() {
	// key
	zSetKey := "language_rank"
	// value Z表示有序集合的成员。
	languages := []redis.Z{
		{Score: 90.0, Member: "Golang"},
		{Score: 95.0, Member: "Python"},
		{Score: 97.0, Member: "Rust"},
		{Score: 99.0, Member: "C/C++"},
		{Score: 88.0, Member: "Java"},
	}
	// WithTimeout返回WithDeadline(parent, time.Now(). add (timeout))。
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	// ZAdd Redis `ZADD key score member [score member ...]` command.
	num, err := rdb.ZAdd(ctx, zSetKey, languages...).Result()
	if err != nil {
		fmt.Printf("zadd failed, err:%v\n", err)
		return
	}
	fmt.Printf("zadd successful num: %v\n", num)

	// ZIncrBy 给某一个元素添加分数值 把Golang的分数加 10
	newScore, err := rdb.ZIncrBy(ctx, zSetKey, 10.0, "Golang").Result()
	if err != nil {
		fmt.Printf("ZIncrBy failed, err:%v\n", err)
		return
	}
	fmt.Printf("ZIncrBy success Golang's score is %f now.\n", newScore)

	// 取分数最高的3个  适用于 排行榜、充值榜...
	// ZRevRangeWithScores according to the Redis documentation, if member does not exist
	// in the sorted set or key does not exist, it will return a redis.Nil error.
	ret, err := rdb.ZRevRangeWithScores(ctx, zSetKey, 0, 2).Result()
	if err != nil {
		fmt.Printf("zRevRangeWithScores failed, err: %v\n", err)
		return
	}
	for _, z := range ret {
		fmt.Printf("z.Member: %v, z.Score: %v\n", z.Member, z.Score)
	}

	// 取95~100分的
	op := &redis.ZRangeBy{
		Min: "95",
		Max: "100",
	}
	ret, err = rdb.ZRangeByScoreWithScores(ctx, zSetKey, op).Result()
	if err != nil {
		fmt.Printf("zrangebyscore failed, err:%v\n", err)
		return
	}
	fmt.Printf("zrangebyscore returned %v\n", ret)
	for _, z := range ret {
		fmt.Printf("ZRangeByScoreWithScores success Member: %v, Score: %v\n", z.Member, z.Score)
	}
}

/**模糊查询，匹配key*/
func ScanKeyDemo(match string) {
	// WithTimeout返回WithDeadline(parent, time.Now(). add (timeout))。
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()
	// 根据前缀查询 Key
	iter := rdb.Scan(ctx, 0, match, 0).Iterator()

	for iter.Next(ctx) {
		fmt.Printf("key value: %v\n", iter.Val())
	}

	if err := iter.Err(); err != nil {
		fmt.Printf("rdb scan failed, err: %v\n", err)
		return
	}
}
